package org.raymond.iworks.study.app;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.raymond.iworks.core.constants.SecurityComponets;
import org.raymond.iworks.repository.entity.TSysUser;
import org.raymond.iworks.repository.repository.TSysUserRepository;
import org.raymond.iworks.repository.utils.RandomUser;
import org.raymond.iworks.StudyApplication;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

@SpringBootTest(classes= StudyApplication.class)
@Slf4j
public class BatchSaveTest {
    private Argon2PasswordEncoder argon2Encoder = SecurityComponets.ARGON2_PASSWORD_ENCODER;
    @Autowired
    private TSysUserRepository tSysUserRepository;

    public List<TSysUser> readyData(){
        int count = 100;
        List<TSysUser> list = new ArrayList<>();
        String pwd = argon2Encoder.encode("123456");
        for(int i=0; i<count; i++){
            TSysUser user = RandomUser.create(pwd);
            list.add(user);
        }
        return list;
    }

    // total:3000,sub:10->9min 13sec
    // total:3000,sub:1000->6min 54sec
    @Test
    public void batchSave1(){
        long start = System.currentTimeMillis();
        ForkJoinPool forkJoinPool = new ForkJoinPool(6);
        List<TSysUser> data = readyData();
        BatchAction batchAction = new BatchAction(data, tSysUserRepository);
        forkJoinPool.invoke(batchAction);
        long end = System.currentTimeMillis();
        log.info("multi cost:{}", (end-start));
    }

    // 3000->6min 41sec
    @Test
    public void batchSave2(){
        long start = System.currentTimeMillis();
        List<TSysUser> data = readyData();
        tSysUserRepository.saveAll(data);
        long end = System.currentTimeMillis();
        log.info("single cost:{}", (end-start));
    }

    static class BatchAction extends RecursiveAction {
        private List list;
        private JpaRepository repository;

        public BatchAction(List list, JpaRepository repository){
            this.list = list;
            this.repository = repository;
        }

        @Override
        protected void compute() {
            if(list.size()>1000){
                //log.info("list.size():{}>10 fork", list.size());
                List<BatchAction> subActions = createSubAction();
                for(BatchAction action : subActions){
                    action.fork();
                    action.join();
                }
            }else{
                //log.info("list.size():{}<=10 save", list.size());
                repository.saveAll(list);
            }
        }

        private List createSubAction(){
            List subAction = new ArrayList<>();
            //将工作负载分成两个子任务
            int list1End = list.size()/2;
            List list1 = list.subList(0, list1End);
            List list2 = list.subList(list1End, list.size());
            BatchAction subAction1 = new BatchAction(list1, repository);
            BatchAction subAction2 = new BatchAction(list2, repository);
            subAction.add(subAction1);
            subAction.add(subAction2);
            //log.info("list1:{}, list2:{}, subAction:{}", list1.size(), list2.size(), subAction.size());
            return subAction;
        }
    }
}
